home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
C/C++ Users Group Library 1996 July
/
C-C++ Users Group Library July 1996.iso
/
vol_300
/
329_01
/
dprintf.c
< prev
next >
Wrap
Text File
|
1980-01-01
|
16KB
|
588 lines
/* > DPRINTF.C
*
* dprintf -- Source Code
* (C) April 4 1990 Asaf Arkin
* All rights reserved
*/
/* Include files:
*/
#include <ctype.h>
#include <setjmp.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Macro constants:- TRUE/FALSE, Flags mask bits, and argument size types.
* Macros:- Maximum and Minimum expand to yield the maximum or minimum of
* two expressions; ToValue gives the decimal value of an ASCII digit and
* ToDigit returns a digit from a value in any radix.
* N.B.: It goes without saying that the macro parameters must have no side
* effects, as they will be carried out more than once.
*/
#define TRUE 1
#define FALSE 0
#define MaskJustify 0x01 /* - Left justify value within field */
#define MaskPlusSign 0x02 /* + Precede positive value with plus */
#define MaskSpace 0x04 /* sp Precede positive value with space */
#define MaskZeros 0x08 /* 0 Justify value with zeros */
#define MaskVariant 0x10 /* # Output value in variant format */
#define TypeNormal 1 /* int/double */
#define TypeShort 2 /* short (meaningless) */
#define TypeLong 3 /* long int */
#define TypeDouble 4 /* long double */
#define Maximum(a,b) ((a)>(b)?(a):(b))
#define Minimum(a,b) ((a)<(b)?(a):(b))
#define ToValue(a) ((a)-'0')
#define ToDigit(a) ((a)<10?(a)+'0':(a)-10+'A')
/* OutFunc (of type dprintf_fp) points to a putchar-like function, which
* performs all output. Called with a character int as parameter, the
* function returns EOF only if an output error occured.
*/
typedef int (*dprintf_fp)(int);
static dprintf_fp OutFunc;
/* Function declarations:
*/
int dprintf(dprintf_fp, const char *, ...);
int vdprintf(dprintf_fp, const char *, va_list);
static void PrintDecimal(long, int, int, char, int *);
static void PrintRadix(unsigned long, int, int, char, char, int *);
static void PrintFloat(long double, int, int, char, char, int *);
static void Print(char *, char *, int, int, char, int *);
static int ToInteger(char **, unsigned long, int, int);
static void dputc(int);
/* dputc employs this longjmp buffer in the event of an output error.
*/
jmp_buf dputc_Buf;
/* int dprintf(dprintf_fp, const char *, ...)
*
* dprintf accepts pointers to a putchar-like function and a format string.
* It then passes them to vdprintf, along with a pointer to the variable
* arguments list.
*/
int dprintf(dprintf_fp Func, const char *Format, ...)
{
int Return;
va_list Args;
va_start(Args,Format);
Return=vdprintf(Func,Format,Args);
va_end(Args);
return Return;
}
/* int vdprintf(dprintf_fp, const char *, va_list)
*
* vdprintf is an implementation of vprintf, as defined in the ANSI
* standard, with an additional pointer-to-function as its first parameter.
* On exit, vdprintf returns the number of characters successfully printed,
* EOF if an error occured.
*/
int vdprintf(dprintf_fp Func, const char *Format, va_list Args)
{
char Flags, Size, *Ptr;
int Width, Precis, OutCnt = 0;
long Int;
unsigned long UnsgInt;
long double Float;
char *FlagsList = "-+ 0#", *TypesList = "hlL";
/* The pointer-to-function assigns to static variable OutFunc, rather than
* being passed as parameter through three layers of functions. The
* longjmp buffer is then initialized, so dputc can return in the event of
* an output error.
*/
OutFunc=Func;
if (setjmp(dputc_Buf))
return EOF;
/* The format string is scanned a character at a time: %'s are processed,
* all other characters are merely echoed to the output.
*/
for (; *Format; ++Format)
{
if (*Format!='%')
{
dputc(*Format);
++OutCnt;
continue;
}
/* An output format can start with a combination of five flags: - + spc
* 0 #. Flags is set accordingly.
*/
if (!*++Format)
return EOF;
Flags=0;
while ((Ptr=strchr(FlagsList,*Format))!=NULL)
{
Flags|=1<<(Ptr-FlagsList);
++Format;
}
/* Read width (zero assumed, if absent) and precision (-1 assumed, if
* absent): width must not start with a zero; precision precedes with a
* period -- if no precision follows the period, zero is assumed; an int
* argument is consumed for the width or precision, if an * replaces the
* value of either.
*/
Width=0;
if (*Format=='*')
{
Width=va_arg(Args,int);
if (Width<0)
{
Width=-Width;
Flags|=MaskJustify;
}
++Format;
}
else
while (isdigit(*Format))
Width=Width*10+ToValue(*Format++);
if (*Format=='.')
{
Precis=0;
if (*++Format=='*')
{
Precis=va_arg(Args,int);
++Format;
}
else
while (isdigit(*Format))
Precis=Precis*10+ToValue(*Format++);
}
else
Precis=-1;
/* An argument is either int (default), short int ('h' -- meaningless),
* long int ('l'), double (default float), or long double ('L').
*/
if ((Ptr=strchr(TypesList,*Format))!=NULL)
{
Size=Ptr-TypesList+TypeShort;
++Format;
}
else
Size=TypeNormal;
/* Consume one output format letter.
* Auxiliary functions process most formats, keeping vdprintf short, or
* else it may fail to compile.
*/
switch (*Format)
{
case 'd':
case 'i':
if (Size==TypeLong)
Int=va_arg(Args,long);
else
Int=va_arg(Args,int);
PrintDecimal(Int,Width,Precis,Flags,&OutCnt);
break;
case 'u':
case 'o':
case 'x': case 'X':
if (Size==TypeLong)
UnsgInt=va_arg(Args,unsigned long);
else
UnsgInt=va_arg(Args,unsigned);
PrintRadix(UnsgInt,Width,Precis,Flags,*Format,&OutCnt);
break;
case 'c':
{
static char Char[2]= {0,0};
/* chars are promoted to int when passed to functions.
*/
Char[0]=va_arg(Args,int);
Print(Char,NULL,Width,-1,Flags,&OutCnt);
break;
}
case 's':
/* If pointer is null, print string "{NULL}".
*/
Ptr=va_arg(Args,char *);
if (Ptr==NULL)
Ptr="{NULL}";
Print(Ptr,NULL,Width,Precis,Flags,&OutCnt);
break;
case 'f':
case 'e': case 'E':
case 'g': case 'G':
if (Size==TypeDouble)
Float=va_arg(Args,long double);
else
Float=va_arg(Args,double);
PrintFloat(Float,Width,Precis,Flags,*Format,&OutCnt);
break;
case 'p':
/* The pointer-to-void argument is cast to long unsigned, assuming
* pointer representation to remain intact.
*/
UnsgInt=(unsigned long) va_arg(Args,void *);
PrintRadix(UnsgInt,Width,Precis,Flags,*Format,&OutCnt);
break;
case 'n':
*(va_arg(Args,int *))=OutCnt;
break;
case '%':
Print("%",NULL,Width,-1,Flags,&OutCnt);
break;
default:
return EOF;
}
}
return OutCnt;
}
/* void PrintDecimal(long, int, int, char, int *)
*
* Print a decimal value (%d or %i) with a sign prefix.
*/
static void PrintDecimal(long Int, int Width, int Precis, char Flags,
int *OutCnt)
{
char *Prefix;
char *Buffer;
if(Int<0)
{
Int=-Int;
Prefix="-";
}
else
{
if (Flags&MaskPlusSign)
Prefix="+";
else
if (Flags&MaskSpace)
Prefix=" ";
else
Prefix=NULL;
}
ToInteger(&Buffer,Int,10,Precis);
Print(Buffer,Prefix,Width,-1,Flags,OutCnt);
free(Buffer);
}
/* void PrintRadix(unsigned long, int, int, char, char, int *)
*
* Print an unsigned int in decimal (%u), octal (%o), hexadecimal (%x or
* %X), or pointer (%p) representation. In the variant fo